import pyaudio
import threading
import random
# get random delay.
# adding some sine waves?
start, end = -10,-1
noise_factor = 0.01
augment_factor = 0.01
def gen_delay():
    return random.randint(start,end)
event = threading.Event()
event.clear()
def wrapper(func,args):
    threading.Thread(target=func,args=args,daemon=True).start()

#chunk = 512 
#chunk = 128
chunk = 1024
sample_format = pyaudio.paInt32
#sample_format = pyaudio.paInt16
channels = 2
fs = 44100
seconds = 1
filename = "sample.wav"

p = pyaudio.PyAudio()
# do this shit with some shift.
stream = p.open(format = sample_format, channels = channels,
                rate = fs, frames_per_buffer = chunk,
                input = True)
stream2 = p.open(format = sample_format, channels = channels,
                 rate = fs, frames_per_buffer = chunk,
                 output = True)
#frames = []
def stream_write(data):
    event.wait()
    stream2.write(data)
    event.clear()

def play(data):
    wrapper(stream_write,(data,))

volume = 1.0
## consider some shift?
import numpy as np
#import time
#setup_time = 1
#time.sleep(setup_time)
#for i in range(0, int(fs / chunk * seconds)):
clear_factor = 0.8
def process(data):
    clear = random.random() > clear_factor
    if clear:
        result = np.zeros((chunk, channels), dtype = np.float32)
        result = result.tobytes()
        event.set()
        play(result)
        return result
    result = np.fromstring(data, dtype=np.float32)
    result = np.reshape(result, (chunk, channels))
    result = -result
    slice0 = result[:,0]*augment_factor
    slice1 = result[:,1]*augment_factor
    delay = gen_delay()
    if delay>0:
        slice0[delay:] = slice0[:-delay]
        slice0[:delay] = np.random.rand(delay)
        slice1[delay:] = slice1[:-delay]
        slice1[:delay] = np.random.rand(delay)
    else:
        slice0[:delay] = slice0[-delay:]
        slice0[delay:] = np.random.rand(-delay)
        slice1[:delay] = slice1[-delay:]
        slice1[delay:] = np.random.rand(-delay)
    slice0 += (np.random.rand(chunk) - 0.5)*noise_factor
    slice1 += (np.random.rand(chunk) - 0.5)*noise_factor
    result[:,0] = slice0
    result[:,1] = slice1
        # padding zeros? or random shits.
#    print(dir(result)) 
#    result = np.reshape(result,(-1,))
#    print(result.shape)
    result = result.tobytes()
#    stream.write(result)
    event.set()
    play(result)

def threaded_process(data):
    wrapper(process, (data,))

for _ in range(300):
    # shall you filter out useless shits.
#for x in range(1000):
#while True:
#    print("range in:", x)
    data = stream.read(chunk)
    threaded_process(data)
#    print("data received!",type(data))
    # no preprocessing?
    # produce anti-pattern immediately.
    # do it in another thread would you?
#    play(result)
#    play(result)
    #anti_data = -data
#    stream2.write(anti_data)
    #play(anti_data)
    # these are bytes.

stream.stop_stream()
stream.close()
#stream2.stop_stream()
#stream2.close()
p.terminate()
    #frames.append(data)
